using System;
using System.Xml;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

using System.IdentityModel.Tokens;
using System.ServiceModel;
using System.Collections.Generic;
using System.ServiceModel.Security;
using Sinacor.Infra.Service.SecurityTokenService.Services.DataContracts;

namespace Sinacor.Infra.Service.SecurityTokenService.Services.ServiceImplementations
{
	#region STSTokenIssuerConfiguration class

	public class STSTokenIssuerConfiguration : ConfigurationElement
    {
        private ConfigurationProperty _serviceTokens = new ConfigurationProperty("serviceTokens", typeof(ServiceTokenCollection), null, ConfigurationPropertyOptions.None);
        private ConfigurationProperty _ttlInSeconds = new ConfigurationProperty("ttlInSeconds", typeof(int), 300, ConfigurationPropertyOptions.None);
        private ConfigurationPropertyCollection _properties = new ConfigurationPropertyCollection();

        private static object _lock = new object();
                      
        public STSTokenIssuerConfiguration() : base()
        {
            _properties.Add(_serviceTokens);
            _properties.Add(_ttlInSeconds);
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get { return _properties; }
        }

        public SecurityToken GetAuthorityToken()
        {
            EndpointAddress endpoint = new EndpointAddress(Constants.Trust.Actions.Issue);
            return GetServiceToken(endpoint);
        }

        public List<SecurityToken> GetAllServiceTokens()
        {
            ServiceTokenCollection serviceTokens = (ServiceTokenCollection)base[_serviceTokens];
            List<SecurityToken> securityTokens = new List<SecurityToken>();

            foreach (ServiceTokenConfiguration serviceToken in serviceTokens)
            {
                X509SecurityToken securityToken = (X509SecurityToken)GetSecurityTokenFromDefinition(serviceToken);
                securityTokens.Add(securityToken);
            }

            return securityTokens;
        }
        
        public SecurityToken GetServiceToken(EndpointAddress appliesTo)
        {
            if (appliesTo == null)
                throw new ArgumentNullException("appliesTo");
            
            if (!((ServiceTokenCollection)base[_serviceTokens]).Contains(appliesTo))
                return null;
            
            ServiceTokenConfiguration serviceToken = ((ServiceTokenCollection)base[_serviceTokens]).Item(appliesTo);

            X509SecurityToken token = GetSecurityTokenFromDefinition(serviceToken);
            
            return token;
        }

       
        public int TtlInSeconds
        {
            get { return (int)base[_ttlInSeconds]; }
        }

        private X509SecurityToken GetSecurityTokenFromDefinition(ServiceTokenConfiguration serviceToken)
        {
            X509Store store = new X509Store(serviceToken.StoreName, serviceToken.StoreLocation);

            X509SecurityToken token = null;
            try
            {
                store.Open(OpenFlags.OpenExistingOnly);
                X509Certificate2Collection collection = store.Certificates.Find(serviceToken.FindType, serviceToken.FindValue, true);

                X509Certificate2 certificate = null;
                if (collection.Count > 0)
                    certificate = collection[0];
                else
                {
                    //throw new Exception(String.Format(Resources.CertificateNotFound, serviceToken.FindValue));
                    throw new Exception(String.Format("The service could not locate the certificate specified in the configuration file {0}", serviceToken.FindValue));
                }

                token = new X509SecurityToken(certificate);

            }
            finally
            {
                store.Close();
            }

            return token;
        }

    }
	#endregion

    #region ServiceTokenCollection class
	
    public class ServiceTokenCollection : ConfigurationElementCollection
    {
        public ServiceTokenCollection() : base()
        {
        }

        public void Add(ConfigurationElement element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            BaseAdd(element);
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new ServiceTokenConfiguration();
        }

        protected override Object GetElementKey(ConfigurationElement element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            ServiceTokenConfiguration serviceTokenConfiguration = (ServiceTokenConfiguration)element;
            return new EndpointAddress(serviceTokenConfiguration.Uri.ToString());
        }

        internal bool Contains(EndpointAddress endpoint)
        {
            return Item(endpoint) != null;
        }

        internal ServiceTokenConfiguration Item(EndpointAddress endpoint)
        {
            foreach (ServiceTokenConfiguration ele in this)
            {
                if (ele.Uri == endpoint.Uri)
                {
                    return ele;
                }
            }

            return null;
        }
	}

    #endregion

	#region ServiceTokenConfiguration class
	public class ServiceTokenConfiguration : ConfigurationElement
    {
        private ConfigurationProperty _uri = new ConfigurationProperty("uri", typeof(Uri), null, ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsKey);
        private ConfigurationProperty _storeLocation = new ConfigurationProperty("storeLocation", typeof(StoreLocation), StoreLocation.LocalMachine, ConfigurationPropertyOptions.None);
        private ConfigurationProperty _storeName = new ConfigurationProperty("storeName", typeof(StoreName), StoreName.My, ConfigurationPropertyOptions.None);
        private ConfigurationProperty _findValue = new ConfigurationProperty("findValue", typeof(string), null, ConfigurationPropertyOptions.None);
        private ConfigurationProperty _findType = new ConfigurationProperty("findType", typeof(X509FindType), X509FindType.FindBySubjectName, ConfigurationPropertyOptions.None); 
        
        private ConfigurationPropertyCollection _properties = new ConfigurationPropertyCollection();

        public ServiceTokenConfiguration()
            : base()
        {
            _properties.Add(_uri);
            _properties.Add(_storeLocation);
            _properties.Add(_storeName);
            _properties.Add(_findValue);
            _properties.Add(_findType);
        }

        protected override ConfigurationPropertyCollection Properties
        {
            get { return _properties; }
        }

        internal StoreLocation StoreLocation
        {
            get { return (StoreLocation)base[_storeLocation]; }
        }

        internal StoreName StoreName
        {
            get { return (StoreName)base[_storeName]; }
        }

        internal string FindValue
        {
            get { return (string)base[_findValue]; }
        }

        internal X509FindType FindType
        {
            get { return (X509FindType)base[_findType]; }
        }
                
        internal Uri Uri
        {
            get { return base[_uri] as Uri; }
        }
	}
	#endregion
}

 
